﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License, as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml.Spreadsheet;

namespace ExtremeML.Spreadsheet.RichText
{
	/// <summary>
	/// Stores the combination of font-related properties for a rich text run.
	/// </summary>
	internal class FontDefinition
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="FontDefinition"/> class.
		/// </summary>
		/// <param name="properties">A <see cref="RunProperties"/> instance from which to import values.</param>
		public FontDefinition(
			RunProperties properties)
		{
			if (properties == null) throw new ArgumentNullException("properties");

			Name = properties.GetFirstChild<RunFont>();
			Family = properties.GetFirstChild<FontFamily>();
			Size = properties.GetFirstChild<FontSize>();
			Color = properties.GetFirstChild<Color>();
			Scheme = properties.GetFirstChild<FontScheme>();
			CharacterSet = properties.GetFirstChild<RunPropertyCharSet>();
		}

	
		/// <summary>
		/// Initializes a new instance of the <see cref="FontDefinition"/> class.
		/// </summary>
		/// <param name="existing">An existing <see cref="FontDefinition"/> on which to base the new instance.</param>
		/// <param name="markup">A string containing markup that defines the properties of the new instance.</param>
		public FontDefinition(
			FontDefinition existing,
			string markup)
		{
			Name = existing.Name;
			Family = existing.Family;
			Size = existing.Size;
			Color = existing.Color;
			Scheme = existing.Scheme;
			CharacterSet = existing.CharacterSet;

			Parse(markup);
		}

		/// <summary>
		/// Gets a <see cref="RunFont"/> that represents the font name.
		/// </summary>
		public RunFont Name { get; private set; }

		/// <summary>
		/// Gets a <see cref="FontFamily"/> that represents the the font family.
		/// </summary>
		public FontFamily Family { get; private set; }

		/// <summary>
		/// Gets a <see cref="FontSize"/> that represents the font size in points.
		/// </summary>
		public FontSize Size { get; private set; }

		/// <summary>
		/// Gets a <see cref="Color"/> that represents the font color.
		/// </summary>
		public Color Color { get; private set; }

		/// <summary>
		/// Gets a <see cref="FontScheme"/> that represents the font scheme.
		/// </summary>
		public FontScheme Scheme { get; private set; }

		/// <summary>
		/// Gets a <see cref="RunPropertyCharSet"/> that represents the font character set.
		/// </summary>
		public RunPropertyCharSet CharacterSet { get; private set; }

		/// <summary>
		/// Applies the values stored in this instance to the passed <see cref="RunProperties"/> instance.
		/// </summary>
		/// <param name="properties">A <see cref="RunProperties"/> instance to be populated.</param>
		public void Populate(
			RunProperties properties)
		{
			properties.RemoveAllChildren<RunFont>();
			properties.RemoveAllChildren<FontFamily>();
			properties.RemoveAllChildren<FontSize>();
			properties.RemoveAllChildren<Color>();
			properties.RemoveAllChildren<FontScheme>();
			properties.RemoveAllChildren<RunPropertyCharSet>();

			if (Name != null) properties.AppendChild((RunFont)Name.Clone());
			if (Family != null) properties.AppendChild((FontFamily)Family.Clone());
			if (Size != null) properties.AppendChild((FontSize)Size.Clone());
			if (Color != null) properties.AppendChild((Color)Color.Clone());
			if (Scheme != null) properties.AppendChild((FontScheme)Scheme.Clone());
			if (CharacterSet != null) properties.AppendChild((RunPropertyCharSet)CharacterSet.Clone());
		}

		/// <summary>
		/// Parses the passed font definition markup string, applying values to the properties of this instance.
		/// </summary>
		private void Parse(
			string markup)
		{
			const string pattern = @"(?<attribute>[\w]+)=""(?<value>[^""]+)""";

			foreach (Match match in Regex.Matches(markup, pattern))
			{
				switch (match.Groups["attribute"].Value.ToLower())
				{
					case "name":
						Name = new RunFont { Val = match.Groups["value"].Value };
						break;
					case "family":
						int family;

						if (int.TryParse(match.Groups["value"].Value, out family))
						{
							Family = new FontFamily { Val = family };
						}

						break;
					case "size":
						double size;

						if (double.TryParse(match.Groups["value"].Value, out size))
						{
							Size = new FontSize { Val = size };
						}

						break;
					case "color":
						var color = match.Groups["value"].Value;

						if (color.StartsWith("#"))
						{
							color = color.Substring(1);
						}

						Color = color.Length == 8 ? new Color {Rgb = color} : new Color { Rgb = "FF" + color };
						break;
					case "scheme":

						switch (match.Groups["value"].Value.ToLower())
						{
							case "none":
								Scheme = new FontScheme {Val = FontSchemeValues.None};
								break;
							case "major":
								Scheme = new FontScheme { Val = FontSchemeValues.Major };
								break;
							case "minor":
								Scheme = new FontScheme { Val = FontSchemeValues.Minor };
								break;
							default:
								break;
						}

						break;
					case "charset":
						int charSet;

						if (int.TryParse(match.Groups["value"].Value, out charSet))
						{
							Family = new FontFamily { Val = charSet };
						}

						break;
					default:
						break;
				}
			}
		}
	}
}
